
lsp = {
	_init_clangd = function(self, cmd)
		local clangd_path = CLANGD_PATH;
		local lspc = edx:create_lsp_client();
		edx._lsp_clients["cpp"] = lspc;
		local build_path = cmd.build_path;
		local toolset = cmd.toolset;
		local query_driver = toolset.home;
		local lsp_env = nil;
		if toolset.tools["clang"] then
			query_driver = make_path(query_driver, "**\\clang++*");
		elseif toolset.tools["gcc"] then
			query_driver = make_path(query_driver, "**\\g++*");
			lsp_env = {["CPATH"] = table.concat(toolset.include, ";"); ["PATH"]=table.concat(toolset.bin, ";")};
		else
			lsp_env = {["CPATH"] = table.concat(toolset.include, ";"); ["PATH"]=table.concat(toolset.bin, ";")};
		end;
		function lspc:on_locate_file(file_name, begin_line, begin_col, end_line, end_col)
			mgr:on_locate_file(1, file_name, begin_line, begin_col, end_line, end_col);
		end;
		lspc.send_empty_command = 1;
		local param_compile_commands_dir = "";
		local param_query_driver = "";
		if build_path then
			param_compile_commands_dir = [[--compile-commands-dir="]]..build_path..[["]];
		end;
		if query_driver then
			param_query_driver = [[ --query-driver="]]..query_driver..[["]];
		end;
		lspc:init(
			clangd_path,
			[[--pch-storage=memory ]]..
			[[--all-scopes-completion ]]..
			-- [[--completion-style=detailed ]]..
			[[--completion-style=bundled ]]..
			[[--include-ineligible-results ]]..
			-- [[--j=8 ]]..
			-- [[--async-preamble ]]..
			[[--limit-results=200 ]]..
			[[--header-insertion-decorators=0 ]]..
			-- [[--sync ]]..
			-- [[--log=verbose ]]..
			[[--background-index=1 ]]..
			param_compile_commands_dir..
			param_query_driver
			, cmd.project_path
			, lsp_env
		);
	end;
	_init_serve_d = function(self, cmd)
		local lspc = edx:create_lsp_client();
		local lsp_env = nil;
		edx._lsp_clients["d"] = lspc;
		lspc:init(
			[[D:\dmd2\windows\bin\serve-d.exe]],
			[[]]
			, cmd.project_path
			, lsp_env
		);
	end;
	_init_lua_lang_server = function(self,cmd)
		local lspc = edx:create_lsp_client();
		local lsp_env = nil;
		-- local project_path = cmd and cmd.project_path;
		local project_path = make_path(mgr.current_document.file_name, "../");
		edx._lsp_clients["lua"] = lspc;
		lspc.send_empty_command = 2;
		lspc:init(
			[[D:\lua-lsp\bin\Windows\lua-language-server.exe]],
			[[-E d:/lua-lsp/main.lua]]
			, project_path
			, lsp_env
		);
		
	end;
	_init_java_lang_server = function(self,cmd)
		local lspc = edx:create_lsp_client();
		local lsp_env = nil;
		-- local project_path = cmd and cmd.project_path;
		local project_path = make_path(mgr.current_document.file_name, "../");
		edx._lsp_clients["java"] = lspc;
		lspc.send_empty_command = 2;
		lspc:init(
			[[java]],
			[[ -Declipse.application=org.eclipse.jdt.ls.core.id1]]..
			[[ -Dosgi.bundles.defaultStartLevel=4]]..
			[[ -Declipse.product=org.eclipse.jdt.ls.core.product]]..
			[[ -Dlog.level=ALL -noverify -Xmx1G]]..
			[[ -jar D:\jdt-language-server\plugins/org.eclipse.equinox.launcher_1.6.300.v20210813-1054.jar]]..
			[[ -configuration D:\jdt-language-server\config_win]]..
			[[ -data D:\jdt-language-server\data]]..
			[[ --add-modules=ALL-SYSTEM]]..
			[[ --add-opens java.base/java.util=ALL-UNNAMED]]..
			[[ --add-opens java.base/java.lang=ALL-UNNAMED]]
			, project_path
			, lsp_env
		);
		
	end;
	_completion_exec = function(self, doc, func)
		if not edx._lsp_clients then
			return false;
		end;
		local content_type_id = content_type_name[doc.content_type];
		local lspc = edx._lsp_clients[content_type_id];
		if not lspc then
			return false;
		end;
		return dbg_call( function()
				return func(lspc);
		end, "lsp error");
	end;
	_try_init_lsp = function(self, working_dir)
		if edx._lsp_clients then
			return;
		end;
		local cmd = find_build_command();
		if cmd and cmd.type ~= "direct" then
			return;
		end;
		if not working_dir then
			working_dir = "";
		end;
		if not cmd then
			cmd = {
				build_path = "";
				toolset = TOOLSETS[1];
				project_path = working_dir;
			};
		end;
		if not cmd.toolset then
			cmd.toolset = TOOLSETS[1];
		end;
		if not cmd.build_path then
			cmd.build_path = "";
		end;
		if not cmd.project_path then
			cmd.project_path = working_dir;
		end;
		self:reset(cmd);
	end;
	terminate = function(self)
		if not edx._lsp_clients then
			return;
		end;
		for name, lspc in pairs(edx._lsp_clients) do
			lspc:terminate();
		end;

		edx._lsp_clients = nil;
	end;
	reset = function(self,cmd)
		self:terminate();
		edx._lsp_clients = {};
		edx:clear("lsp");

		-- init clangd service
		self:_init_clangd(cmd);

		-- init serve-d service
		-- self:_init_serve_d(cmd);
		
		-- self:_init_lua_lang_server(cmd);

		-- self:_init_java_lang_server(cmd);

		local all_doc = {mgr.get_all_documents()};
		local def_target = menu_bar.get_cmake_default_target();
		for idx, doc in ipairs(all_doc) do
			local content_type_id = content_type_name[doc.content_type];
			local lspc = edx._lsp_clients[content_type_id];
			if lspc then
				lspc:did_open_doc(doc, content_type_id);
			end;
		end;
	end;
	doc_saved = function(self, doc)
		self:_completion_exec(doc, function(lspc) return lspc:did_save_doc(doc); end);
	end;
	doc_opened = function(self, doc)
		self:_try_init_lsp();
		local content_type_id = content_type_name[doc.content_type];
		self:_completion_exec(doc, function(lspc) return lspc:did_open_doc(doc, content_type_id); end);
	end;
	doc_closed = function(self, doc)
		self:_completion_exec(doc, function(lspc) return lspc:did_close_doc(doc); end);
	end;
	find_signature_help = function(self, doc, helper)
		if self:_completion_exec(doc, function(lspc) return lspc:find_signature_help(doc, helper) or (helper.cancel_completion() and false); end) then
			return true;
		end;
		return false;
	end;
	do_completion = function(self, doc, helper)
		if self:_completion_exec(doc, function(lspc) return lspc:do_complete(doc, helper) or (helper.cancel_completion() and false); end) then
			return true;
		end;
		return false;
	end;
	update_completion_tip = function(self, doc, helper)
		if self:_completion_exec(doc, function(lspc) return lspc:update_completion_list_tip(doc, helper) or (helper.cancel_completion() and false); end) then
			return true;
		end;
		return false;
	end;
	cleanup_completion = function(self, doc)
		if edx._lsp_clients then
			if doc then
				local content_type_id = content_type_name[doc.content_type];
				local lspc = edx._lsp_clients[content_type_id];
				if lspc then
					lspc:clean_completion_list();
				end;
			else
				for idx, lspc in ipairs(edx._lsp_clients) do
					lspc:clean_completion_list();
				end;
			end;
		end;
	end;
	commit_completion = function(self, doc, helper)
		if(self:_completion_exec(doc, function(lspc) return lspc:commit_completion(doc, helper); end)) then
			return true;
		end;
		return false;
	end;
	jump_placeholder = function(self, doc)
		if(self:_completion_exec(doc, function(lspc) return lspc:jump_placeholder(doc); end)) then
			return true;
		end;
		return false;
	end;
	hover_info = function(self, doc, line, col)
		if line == nil then
			line = -1;
		end;
		if col == nil then
			col = -1;
		end;
		if(self:_completion_exec(doc, function(lspc) return lspc:find_hover_info(doc, line, col); end)) then
			return true;
		end;
		return false;
	end;
	find_declaration = function(self, doc)
		if(self:_completion_exec(doc, function(lspc) return lspc:find_declaration(doc); end)) then
			return true;
		end;
		return false;
	end;
	find_definition = function(self, doc)
		if(self:_completion_exec(doc, function(lspc) return lspc:find_definition(doc); end)) then
			return true;
		end;
		return false;
	end;
	find_implementation = function(self, doc)
		if(self:_completion_exec(doc, function(lspc) return lspc:find_implementation(doc); end)) then
			return true;
		end;
		return false;
	end;
	find_reference = function(self, doc)
		if(self:_completion_exec(doc, function(lspc) return lspc:find_reference(doc); end)) then
			return true;
		end;
		return false;
	end;
	list_doc_structure = function(self, doc, class_view)
		if(self:_completion_exec(doc, function(lspc) return lspc:list_doc_structure(doc, class_view); end)) then
			return true;
		end;
		return false;
	end;
};
